<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
    </style>
</head>

<body>
    <canvas id="canvas"></canvas>
</body>
<script id="NormalVS" type="x-shader/x-vertex">
    attribute vec4 a_Position;
    uniform mat4 u_PvM4;
    void main(){
        gl_Position = u_PvM4 * a_Position;
        gl_PointSize = 20.0;
    }
</script>
<script id="NormalFS" type="x-shader/x-fragment">
    precision mediump float;
    uniform vec4 u_Color;
    void main(){
        gl_FragColor = u_Color;
    }
</script>
<script id="LightVS" type="x-shader/x-vertex">
    attribute vec4 a_Position;
    attribute vec3 a_Normal;
    uniform float u_PointSize;
    uniform mat4 u_ModelM4;
    uniform mat4 u_PvM4;
    varying vec3 v_Normal;
    varying vec3 v_Position;
    void main(){
        gl_Position = u_PvM4 * u_ModelM4 * a_Position;
        gl_PointSize = u_PointSize;
        v_Normal = a_Normal;
        v_Position = vec3(a_Position);
    }
</script>
<script id="LightFS" type="x-shader/x-fragment">
    precision mediump float;
    uniform float u_Kd;//漫反射系数
    uniform float u_Ks;//高光反射系数
    uniform float u_Ka;//环境反射系数

    uniform vec3 u_Eye;//视点
    uniform vec3 u_LightEye;//灯视点
    uniform vec3 u_LightLookAt;//灯目标点
    uniform float u_Intensity;//灯光强度
    uniform vec3 u_Dist;//衰减 起始距离/结束距离/反向衰减距离(光线的长度)
    uniform vec2 u_Fov;//衰减 起始范围/结束范围(光线的宽度)

    uniform vec3 u_Color;//物体颜色
    uniform vec3 u_KsColor;//高光颜色
    
    varying vec3 v_Position;//位置
    varying vec3 v_Normal;//法线
    void main(){


        //光线方向
        vec3 lightDir = normalize(u_LightEye - u_LightLookAt);
        //逐片元作色
        vec3 normal = normalize(v_Normal);
        //眼睛看作色点的视线
        vec3 eyeDir = normalize(u_Eye - v_Position);
        //视线与光线的角平分线
        vec3 h = normalize(eyeDir + lightDir);

        //漫反射
        vec3 Ld = u_Kd * u_Color * max(0.0, dot(normal, lightDir));
        //镜面反射
        vec3 Ls = u_Ks * u_KsColor * pow(max(0.0, dot(normal, h)), 64.0);
        //漫反射
        vec3 La = u_Ka * vec3(1.0, 1.0, 1.0);
        
        //着色点到光线起始点的向量
        vec3 pl = u_LightEye - v_Position;
        //着色点到光线方向垂线的距离
        float r = length(cross(pl, lightDir));
        //着色点到光线的距离
        float dist = dot(pl, lightDir);
        //衰减的扩散垂线
        float fov1 = tan(u_Fov.x) * dist;
        float fov2 = tan(u_Fov.y) * dist;
        //宽度衰减范围
        float fallY = 1.0 - smoothstep(fov1, fov2, r);
        //长度衰减范围
        float fallX = 1.0 - smoothstep(u_Dist.x, u_Dist.y, dist);
        //反向衰减
        float fallB = step(0.0, dist);

        
        //筒灯的亮度
        float Intensity = u_Intensity * fallY * fallX * fallB;

        //Blinn-Phong反射
        vec3 color = (Ld + Ls) * Intensity + La;

        gl_FragColor = vec4(color, 1.0);
    }
</script>
<script type="module">
    import { Matrix4, Vector3, Color, SphereGeometry, PerspectiveCamera, } from '../js/three.module.js';
    import { CreateProgram, imgPromise } from '../js/Utils.js';
    import lib from '../js/ThreeLib.js';
    import CameraController from '../js/CameraController.js'
    import Sphere from '../js/Sphere.js'
    import Control from '../js/Control.js'
    import { getVerticeNormals } from '../js/ShadingFrequency.js'
    import Backdrop from '../js/Light/Backdrop.js'

    import Geo from '../js/Poly2/Geo.js'
    import Mat from '../js/Poly2/Mat.js'
    import Obj3D from '../js/Poly2/Obj3D.js'
    import Scene from '../js/Poly2/Scene.js'

    //#region gl
    const canvas = document.getElementById("canvas");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight - 4;

    const gl = canvas.getContext("webgl", { antialias: true, });

    gl.enable(gl.BLEND);
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    gl.enable(gl.DEPTH_TEST);
    // gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);//去图片描边
    //#endregion

    const scene = new Scene({ gl: gl });
    scene.regProgram('light', {
        program: CreateProgram(gl,
            document.querySelector('#LightVS').innerText,
            document.querySelector('#LightFS').innerText),
        attribs: ['a_Position', 'a_Normal'],
        uniforms: ['u_PvM4', 'u_ModelM4', 'u_PointSize', 'u_Color', 'u_KsColor',
            'u_Kd', 'u_Ks', 'u_Ka', 'u_Eye',
            'u_LightEye', 'u_LightLookAt', 'u_Intensity', 'u_Dist', 'u_Fov']
    });
    scene.regProgram('normal', {
        program: CreateProgram(gl,
            document.querySelector('#NormalVS').innerText,
            document.querySelector('#NormalFS').innerText),
        attribs: ['a_Position'],
        uniforms: ['u_PvM4', 'u_Color']
    });


    const pvM4 = new Matrix4();
    const modelM4 = new Matrix4();

    const camera = new PerspectiveCamera(45, canvas.width / canvas.height, 1, 20);
    const cameraController = new CameraController({
        canvas,
        camera,
        pvMatrix: pvM4
    });



    const Kd = 0.7;//漫反射系数
    const Ks = 0.2;//镜面反射系数
    const Ka = 0.2;//环境反射系数
    const color = [1, 1, 1];//物体颜色
    const ksColor = [1, 1, 1];//物体高光颜色
    const eye = [...cameraController.eye];

    const lightEye = [0, 2, -0.7];
    const lightLookAt = [0, 0, 0];
    const intensity = 1;
    const dist = [0, 10, -1];
    const fov = [20 * Math.PI / 180, 50 * Math.PI / 180];


    //Axis
    {
        scene.add(new Obj3D().createAxis(gl, 'normal'));
    }


    //幕布
    {
        const backdrop = new Backdrop();
        const vertices = backdrop.vertices;
        const elements = backdrop.elements;

        const normals = backdrop.normals;

        //物体
        {
            let obj01 = new Obj3D({
                geo: new Geo({
                    data: {
                        'a_Position': { size: 3, array: new Float32Array(vertices) },
                        'a_Normal': { size: 3, array: new Float32Array(normals) },
                    },
                    element: { array: new Uint16Array(elements) }
                }),
                mat: new Mat({
                    program: 'light',
                    mode: ['TRIANGLES'],
                    data: {
                        'u_PvM4': { type: 'uniformMatrix4fv', value: pvM4.elements },
                        'u_ModelM4': { type: 'uniformMatrix4fv', value: modelM4.elements },
                        'u_Kd': { type: 'uniform1f', value: Kd },
                        'u_Ks': { type: 'uniform1f', value: Ks },
                        'u_Ka': { type: 'uniform1f', value: Ka },
                        'u_Eye': { type: 'uniform3fv', value: eye },
                        'u_Color': { type: 'uniform3fv', value: color },
                        'u_KsColor': { type: 'uniform3fv', value: ksColor },
                        'u_LightEye': { type: 'uniform3fv', value: lightEye },
                        'u_LightLookAt': { type: 'uniform3fv', value: lightLookAt },
                        'u_Intensity': { type: 'uniform1f', value: intensity },
                        'u_Dist': { type: 'uniform3fv', value: dist },
                        'u_Fov': { type: 'uniform2fv', value: fov },
                    },
                }),
            });
            scene.add(obj01);

        }

        //法线
        {
            const normalArr = getNormalize(vertices, normals);
            let normalObj = new Obj3D({
                geo: new Geo({
                    data: {
                        'a_Position': { size: 3, array: new Float32Array(normalArr) },
                    },
                }),
                mat: new Mat({
                    program: 'normal',
                    mode: ['LINES'],
                    data: {
                        'u_PvM4': { type: 'uniformMatrix4fv', value: pvM4.elements },
                        'u_Color': { type: 'uniform4fv', value: [1, 1, 1, 1] },
                    },
                }),
            });
            //scene.add(normalObj);
        }
    }


    //球
    {
        const w = 10;
        const h = 10;
        const mySphere = new Sphere(0.5, w, h);

        const vertices = mySphere.vertices;
        const elements = mySphere.elements;
        const normals = getVerticeNormals(vertices, elements, 1);
        //物体
        {
            let obj01 = new Obj3D({
                geo: new Geo({
                    data: {
                        'a_Position': { size: 3, array: new Float32Array(vertices) },
                        'a_Normal': { size: 3, array: new Float32Array(normals) },
                    },
                    element: { array: new Uint16Array(elements) }
                }),
                mat: new Mat({
                    program: 'light',
                    mode: ['TRIANGLES'],
                    data: {
                        'u_PvM4': { type: 'uniformMatrix4fv', value: pvM4.elements },
                        'u_ModelM4': { type: 'uniformMatrix4fv', value: modelM4.elements },
                        'u_Kd': { type: 'uniform1f', value: Kd },
                        'u_Ks': { type: 'uniform1f', value: Ks },
                        'u_Ka': { type: 'uniform1f', value: Ka },
                        'u_Eye': { type: 'uniform3fv', value: eye },
                        'u_Color': { type: 'uniform3fv', value: color },
                        'u_KsColor': { type: 'uniform3fv', value: ksColor },
                        'u_LightEye': { type: 'uniform3fv', value: lightEye },
                        'u_LightLookAt': { type: 'uniform3fv', value: lightLookAt },
                        'u_Intensity': { type: 'uniform1f', value: intensity },
                        'u_Dist': { type: 'uniform3fv', value: dist },
                        'u_Fov': { type: 'uniform2fv', value: fov },
                    },
                }),
            });
            scene.add(obj01);
        }

        //法线
        {
            const normalArr = getNormalize(vertices, normals);
            let normalObj = new Obj3D({
                geo: new Geo({
                    data: {
                        'a_Position': { size: 3, array: new Float32Array(normalArr) },
                    },
                }),
                mat: new Mat({
                    program: 'normal',
                    mode: ['LINES'],
                    data: {
                        'u_PvM4': { type: 'uniformMatrix4fv', value: pvM4.elements },
                        'u_Color': { type: 'uniform4fv', value: [1, 1, 1, 1] },
                    },
                }),
            });
            //scene.add(normalObj);
        }

    }








    draw();


    let set = 0;
    (function ani() {
        // set += 1;

        draw();
        requestAnimationFrame(ani);
    })();

    function draw() {
        // scene.setUniform('u_LightEye', { value: [...cameraController.eye] });
        // scene.setUniform('u_Eye', { value: [...cameraController.eye] });

        scene.setUniform('u_PvM4', { value: pvM4.elements });
        scene.draw();
    }


    function getNormalize(nodeArr, normals, length = 0.2) {
        const normalArr = [];
        for (let i = 0; i < normals.length; i += 3) {
            const curNormal = new Vector3(normals[i], normals[i + 1], normals[i + 2]);
            const node1 = new Vector3(nodeArr[i], nodeArr[i + 1], nodeArr[i + 2]);

            normalArr.push(...node1)
            normalArr.push(...curNormal.clone().setLength(length).add(node1));
        }
        return normalArr;
    }

</script>

</html>